<template>
  <div class="p-3 my-container position-relative">
    <div class="d-flex w-100 h-100">
      <div id="mynetwork" ref="mynetwork"></div>
    </div>
    <div class="position-absolute" style="bottom: 40px;left: 30px;">
      <el-button type="primary" size="medium" @click="goBack">返回</el-button>
      <el-button type="primary" size="medium" @click="addHandDeviceGradeInit">保存</el-button>
    </div>
    <div id="myRightMenuId" class="myMuen" v-show="showMenu" :style="styleFun">
      <div>
        <el-button type="primary" size="mini" icon="el-icon-plus" @click="addNode"></el-button>
      </div>
      <div class="mt-1 mb-1">
        <el-button type="primary" size="mini" icon="el-icon-zoom-in" @click="updataNode"></el-button>
      </div>
      <div>
        <el-button type="danger" size="mini" icon="el-icon-delete" @click="deleteNode"></el-button>
      </div>
    </div>

    <yc-dialog ref="ycDialog" dialogTitle="选择设备"
               dialogWidth="30%" :show-center="false" @close="dialogClose">
      <template #content>
        <div class="dialog-content">
          <el-form ref="loginFormRef" :rules="newAccountModelRules" :model="newAccountModel" label-width="70px">
            <el-form-item label="设备" prop="deviceCode">
              <el-select style="width: 100%;" clearable v-model="newAccountModel.deviceCode">
                <el-option v-for="item in templateNodesList" :key="item.deviceCode" :label="item.deviceName"
                           :value="item.deviceName + '&&' + item.deviceCode"/>
              </el-select>
            </el-form-item>
          </el-form>
        </div>
      </template>
      <template #footer>
        <el-button type="primary" @click="dialogSave">确定</el-button>
        <el-button @click="$refs.ycDialog.dialogVisible = false">取消</el-button>
      </template>
    </yc-dialog>
  </div>
</template>

<script>
  const vis = require("vis-network/dist/vis-network.min.js")
  import ycDialog from "@/components/pageDialog"
  import {
    queryDeviceGradeList,
    queryOneDeviceGradeMain,
    addAndUpdateHandDeviceGradeInit
  } from "@/api/initializeVis/index.js"

  export default {
    components: {
      ycDialog
    },
    data() {
      return {
        mainCode: "",
        deviceGradeList: [],
        nodes: null,
        edges: null,
        options: {
          autoResize: true, // 默认true,自动调整容器的大小
          height: "100%", // 默认值
          width: "100%",  // 默认值
          locale: "cn",   // 选择语言，默认英文en，cn为汉语
          locales: {      // 语言格式化对象
            cn: {
              edit: "编辑",
              del: "删除",
              back: "返回",
              addNode: "添加节点",
              addEdge: "添加连线",
              editNode: "编辑节点",
              editEdge: "编辑连线",
              addDescription: "点击空白区域添加节点",
              edgeDescription: "Click on a node and drag the edge to another node to connect them.",
              editEdgeDescription: "Click on the control points and drag them to a node to connect to it.",
              createEdgeError: "Cannot link edges to a cluster.",
              deleteClusterError: "Clusters cannot be deleted.",
              editClusterError: "Clusters cannot be edited."
            }
          },
          // 配置模块
          configure: {
            enabled: false // false时不会在界面上出现各种配置项
          },
          // 节点模块
          nodes: {
            chosen: true, // 对选择节点做出反应
            color: {
              border: "#2B7CE9",
              background: "#97C2FD",
              highlight: {
                border: "#2B7CE9",
                background: "#FFEC8B"
              },
              hover: {
                border: "#2B7CE9",
                background: "#FFC125"
              }
            },
            font: {
              align: "left",
              color: "#0953e8",
              size: 12,
              vadjust: 5 // 标签文本的垂直位置，值越大离节点越远
            },
            labelHighlightBold: false,
            // hidden: true, // 为true不会显示节点。但仍是物理模拟的一部分
            shape: "image",
            image: {    // 路径问题要注意，一定要存储在静态文件夹中
              unselected: "/static/img/断路器.png"
              // selected: '/static/images/icon_selected.svg',
            },
            size: 20, // 节点大小
            imagePadding: 35,
            physics: false, // 关闭物理引擎
            title: "", // 用户悬停在节点上时显示的标题,可以是HTML元素或包含纯文本或HTML的字符串
            widthConstraint: { // 节点的最小宽度与最大宽度
              maximum: 100
            }
          },
          // 边模块
          edges: {
            // label: '',
            physics: false,
            smooth: {
              enabled: true,
              // type: 'curvedCCW', // 平滑曲线的类型
              forceDirection: "horizontal" // 用于分层布局的配置项,可选值有: ['horizontal', 'vertical', 'none']
            }
            // arrows: {  // 这里可以用来自定义箭头，type为image类型即可
            // middle: { enabled: true, type: 'image', imageHeight: 12, imageWidth: 12, src: getOpticalRed() }
            // },
          },
          // 交互模块
          interaction: {
            hover: true, // 启用鼠标悬停
            hoverConnectedEdges: false, // 鼠标悬停在节点上时，与其连接的边不高亮显示
            hideEdgesOnDrag: false, // true时拖动视图时不会绘制边。这会加快拖动时的响应速度
            hideNodesOnDrag: false, // true时拖动视图时不会绘制节点
            navigationButtons: true,
            selectConnectedEdges: false, // 选中节点时，与其连接的边不高亮
            multiselect: false, // true时长时间单击（或触摸）以及控件单击将添加到选择中
            tooltipDelay: 100,
            keyboard: true
          },
          // 可视化操作: 没起作用，不知道是不是版本的问题
          manipulation: {
            enabled: false,
            initiallyActive: true,
            addNode: true,
            addEdge: true,
            // editNode: undefined,
            editEdge: true,
            deleteNode: true,
            deleteEdge: true,
            controlNodeStyle: {
              // all node options are valid.
            }
          },
          // 物理引擎
          physics: {
            enabled: true,
            barnesHut: {
              gravitationalConstant: -20000, // 斥力
              springLength: 20,   // 弹簧长度
              avoidOverlap: 1
            },
            maxVelocity: 50,
            minVelocity: 1,
            stabilization: {
              iterations: 500, // 最大迭代次数
              enabled: true,
              // fit: true,
              fit: false   // 值为false时，点击刷新后可回到刷新前的页面
            }
          },
          // 布局
          layout: {
            randomSeed: 1000,
            hierarchical: {
              enabled: false,
              levelSeparation: 100, // 层级之间的距离,太小的话箭头会盖住标签字
              nodeSpacing: 100,     // 节点之间的距离
              treeSpacing: 100,     // 树之间的距离
              sortMethod: "directed"
            }
          }
        },
        network: null,
        showMenu: false,
        selectedNodeId: "",
        nodeItem: {},
        menuLeft: "",
        menuTop: "",
        selectNodesList: [],
        templateNodesList: [],
        nodesList: [],
        edgesList: [],

        newAccountModel: {
          deviceCode: ""
        },
        newAccountModelRules: {
          deviceCode: [{ required: true, message: "请选择设备", trigger: "change" }]
        }
      }
    },
    async created() {
      this.mainCode = this.$route.query.mainCode
      // await this.queryOneDeviceGradeMain()
    },
    async mounted() {
      if (this.mainCode) {
        await this.queryOneDeviceGradeMain()
      } else {
        this.nodesList[0] = { id: 1, label: "根节点", deviceCode: "", level: 1, x: -52, y: -270 }
      }

      await this.init()
      this.$nextTick(() => {
        this.setNodeSize(this.network.getScale())
      })
      await this.queryDeviceGradeList()
    },
    methods: {
      async queryOneDeviceGradeMain() {
        const { object } = await queryOneDeviceGradeMain({
          mainCode: this.mainCode
        })
        if (object.position) {
          const testObj = JSON.parse(object.position)
          this.selectNodesList = testObj.nodesList

          this.nodesList = testObj.nodesList
          this.edgesList = testObj.edgesList
        }
      },

      async queryDeviceGradeList() {
        const { object } = await queryDeviceGradeList()
        this.deviceGradeList = object
        this.templateNodesList = object
      },

      init() {
        this.nodes = new vis.DataSet(this.nodesList)
        this.edges = new vis.DataSet(this.edgesList)
        const container = this.$refs.mynetwork
        const data = {
          nodes: this.nodes,
          edges: this.edges
        }
        console.log(this.nodesList)
        const options = this.options
        this.network = new vis.Network(container, data, options)

        // 事件监听
        this.networkEvent()
      },
      // 拓扑网络监听事件
      networkEvent() {
        const _this = this
        //单击
        _this.network.on("click", (e) => {
          _this.hideContextMenu()
        })
        // 单击鼠标右键
        _this.network.on("oncontext", (e) => {
          // 获取节点 id
          let nodeId = _this.network.getNodeAt(e.pointer.DOM)
          e.event.preventDefault()
          if (nodeId) {
            _this.showMenu = true
            _this.selectedNodeId = nodeId
            _this.nodeItem = _this.mapArr(_this.nodesList, nodeId)[0]

            _this.changeMenuPosition(e)
          } else {
          }
        })

        _this.network.on("dragEnd", function(params) {
          let nodeId = params.nodes[0]
          if (nodeId) {
            let nodePosition = _this.network.getPositions([nodeId])
            let x = nodePosition[nodeId].x
            let y = nodePosition[nodeId].y
            console.log("Node " + nodeId + " new position: x = " + x + ", y = " + y)
            for (let i = 0; i < _this.nodesList.length; i++) {
              if (nodeId == _this.nodesList[i].id) {
                _this.nodesList[i].x = x
                _this.nodesList[i].y = y
              }
            }
          }
        })

      },
      setNodeSize(scale) {
        this.options.nodes.size = (20 / scale)
        this.options.nodes.font.size = (12 / scale)
        this.network.setOptions(this.options)
      },
      mapArr(list, id) {
        let itemObj = list.filter(item => item.id == id)
        return itemObj
      },
      // 更新右键菜单位置
      changeMenuPosition(e) {
        let nodePosition = this.getNodePosition(e)
        /*
         *转换的坐标为相对容器DOM的坐标
         *82菜单半径
         */
        this.menuLeft = nodePosition.x + 41
        this.menuTop = nodePosition.y - 8
      },

      hideContextMenu() {
        this.showMenu = false
      },
      getNodePosition() {
        // console.log(e);
        let nodeId = this.selectedNodeId
        // 通过节点id 获取节点中心点的在画布的位置
        let nodePosition = this.network.getPositions(nodeId)
        // 坐标转化
        nodePosition = this.network.canvasToDOM(nodePosition[nodeId])
        return nodePosition
      },
      // 添加节点
      addNode(id, label, toId) {
        let testId = new Date().getTime()
        let newNode = {
          id: testId,
          label: "临时节点",
          deviceCode: "",
          level: this.nodeItem.level + 1,
          x: this.nodeItem.x,
          y: this.nodeItem.y + 100
        }
        let newLine = { from: this.selectedNodeId, to: testId }

        this.nodesList.push(newNode)
        this.edgesList.push(newLine)

        // this.nodes.add(newNode)
        // this.edges.add(newLine)
        this.init()
        this.hideContextMenu()
      },
      updataNode() {
        if (this.nodeItem.deviceCode) {
          this.newAccountModel.deviceCode = this.nodeItem.label + "&&" + this.nodeItem.deviceCode
          for (let i = 0; i < this.selectNodesList.length; i++) {
            if (this.selectNodesList[i].deviceCode == this.nodeItem.deviceCode) {
              this.selectNodesList.splice(i, 1)
            }
          }
        } else {
          this.newAccountModel.deviceCode = ""
        }
        this.hideContextMenu()
        this.$refs.ycDialog.dialogVisible = true
      },

      dialogSave() {
        this.$refs.loginFormRef.validate(async (v) => {
          if (v) {
            let deviceName = this.newAccountModel.deviceCode.split("&&")[0]
            let deviceCode = this.newAccountModel.deviceCode.split("&&")[1]
            let tempObj = {
              id: this.selectedNodeId,
              deviceCode: deviceCode,
              label: deviceName,
              level: this.nodeItem.level,
              x: this.nodeItem.x,
              y: this.nodeItem.y
            }
            for (let i = 0; i < this.nodesList.length; i++) {
              if (this.selectedNodeId == this.nodesList[i].id) {
                this.nodesList.splice(i, 1, tempObj)
              }
            }
            this.selectNodesList.push(tempObj)

            this.init()
            this.$refs.ycDialog.dialogVisible = false
          }
        })
      },
      dialogClose() {
        let deviceName = this.newAccountModel.deviceCode.split("&&")[0]
        let deviceCode = this.newAccountModel.deviceCode.split("&&")[1]
        let tempObj = {
          id: this.selectedNodeId,
          deviceCode: deviceCode,
          label: deviceName,
          level: this.nodeItem.level,
          x: this.nodeItem.x,
          y: this.nodeItem.y
        }
        const isShow = this.selectNodesList.some(item => item.deviceCode == deviceCode)
        if (!isShow) {
          this.selectNodesList.push(tempObj)
        }
      },
      deleteNode() {
        if (this.selectedNodeId == 1) {
          this.$message({
            message: "此数据不可删除",
            type: "warning"
          })
        } else {
          for (let i = 0; i < this.edgesList.length; i++) {
            if (this.edgesList[i].from == this.selectedNodeId) {
              return this.$message({
                message: "此数据子节点，不可删除",
                type: "warning"
              })
            }
          }

          for (let i = 0; i < this.edgesList.length; i++) {
            if (this.edgesList[i].to == this.selectedNodeId) {
              this.edgesList.splice(i, 1)
            }
          }
          this.nodesList = this.filterArr(this.nodesList, this.selectedNodeId)
          this.init()
          this.hideContextMenu()
        }
      },
      filterArr(list, id) {
        list = list.filter(item => item.id !== id)
        return list
      },
      // 函数封装
      resArr(arr1, arr2) {
        return arr1.filter((v) => arr2.every((val) => val.deviceCode != v.deviceCode))
      },
      addHandDeviceGradeInit() {
        const obj = {
          nodesList: this.nodesList,
          edgesList: this.edgesList,
          mainCode: this.mainCode
        }
        addAndUpdateHandDeviceGradeInit({
          addJsonConfig: JSON.stringify(obj)
        }).then(res => {
          if (res.status == 200) {
            this.$message({
              message: "手动设置成功",
              type: "success"
            })
            this.$router.go(-1)
          }
        })
      },
      goBack() {
        this.$router.go(-1)
      }
    },
    computed: {
      styleFun: function() {
        let str = `left: ${this.menuLeft}px; top: ${this.menuTop}px;`
        return str
      }
    },
    watch: {
      selectNodesList(newValue, oldValue) {
        let newArr = this.resArr(this.deviceGradeList, newValue)
        this.templateNodesList = newArr
      },
      immediate: true
    }
  }
</script>

<style scoped>
  #mynetwork {
    width: 100%;
    height: 100%;
    border: 1px solid #eee7e7;
  }

  .myMuen {
    position: absolute;
    /* width: 164px;
    height: 164px; */
  }

  .myMuen div {
    cursor: pointer;
    /*border: 1px solid #4d4d4d;*/
  }


  ::v-deep .vis-navigation {
    position: absolute;
    z-index: 2;
    right: 12px;
    top: 47.8%;
  }

  .vis-zoomIn,
  .vis-zoomOut {
    width: 24px;
    height: 24px;
    margin: 4px;
    cursor: pointer;
    border: 1px solid #c0c0c0;
    border-radius: 2px;
  }

  .vis-zoomIn,
  .vis-zoomOut

  &
  :hover {
    border-color: #ccc;
    color: #4d4d4d;
  }

  ::v-deep .vis-tooltip {
    position: absolute;
    visibility: visible;
  }

  .mybotton {
    width: 60px;
    height: 30px;
    line-height: 30px;
    cursor: pointer;
    border: 1px solid #888;
    border-radius: 5px;
  }

  .my_line {
    width: 4px;
    height: 22px;
    background-color: #3276ff;
    margin-right: 10px;
    border-radius: 4px;
  }

  .ipt {
    font-size: 16px;
  }
</style>



